/********************************************************************/
/*          main.c                                                  */
/*            multi_sio (Multi-play Communication Sample) Main      */
/*                                                                  */
/*          Copyright (C) 1999-2001 NINTENDO Co.,Ltd.               */
/********************************************************************/
//#define CODE32
#include "MultiSio.h"
#include "data.h"

//#define KEY_ON      // Flag to enable OBJ key operation


/*-------------------- Global Variable ----------------------------*/

    u16      Cont, Trg;                         // Key Input

    u32      IntrMainBuf[0x100];                // Interrupt Main Routine Buffer

    u16      BgBak[32*32];                      // BG  Backup
    OamData  OamBak[128];                       // OAM Backup

    u16      UsrSendBuf[MULTI_SIO_BLOCK_SIZE/2];    // User Send Buffer
    u16      UsrRecvBuf[MULTI_SIO_PLAYERS_MAX][MULTI_SIO_BLOCK_SIZE/2];
                                                    // User Receive Buffer

    u32      SioFlags;                              // Communication State
    u8       SioStartFlag;                          // Communication Start Flag

/*---------------------- Subroutine    -----------------------------*/

void KeyRead(void);
void BgScSet(u8 *Srcp, u16 *Destp, u8 PlttNo);


/*------------------------------------------------------------------*/
/*                      Interrupt Table                             */
/*------------------------------------------------------------------*/
typedef void (*IntrFuncp)(void);
void IntrDummy(void);
void VBlankIntr(void);

const IntrFuncp IntrTable[13] = {
    VBlankIntr,                 // V-Blank Interrupt
#ifdef MULTI_SIO_DI_FUNC_FAST
    (IntrFuncp )IntrFuncBuf + 1,// Serial Communication Interrupt
#else                           // or Multi-play Communication Timer Interrupt
    MultiSioIntr,
#endif

    IntrDummy,                  // H-Blank Interrupt
    IntrDummy,                  // V Counter Matching Interrupt
    IntrDummy,                  // DMA0 Interrupt
    IntrDummy,                  // DMA1 Interrupt
    IntrDummy,                  // DMA2 Interrupt
    IntrDummy,                  // DMA3 Interrupt
    IntrDummy,                  // Key Interrupt
};

IntrFuncp IntrTableBuf[14];


/*==================================================================*/
/*                      Main Routine                                */
/*==================================================================*/
extern void intr_main(void);

void AgbMain(void)
{
    u32    MultiSioId;
    char  *StrTmp;
    s32    i, ii;

    RegisterRamReset(RESET_EX_WRAM_FLAG                    // Clear CPU External Work RAM
                   | RESET_CPU_WRAM_FLAG);                 // Clear CPU Internal Work RAM

    *(vu16 *)REG_WAITCNT = CST_ROM0_1ST_3WAIT | CST_ROM0_2ND_1WAIT
                         | CST_PREFETCH_ENABLE;            // 3-1 Wait Access Setting

    DmaCopy(3, IntrTable, IntrTableBuf,sizeof(IntrTableBuf),32);// Set Interrupt Table
    DmaCopy(3, intr_main, IntrMainBuf, sizeof(IntrMainBuf), 32);// Set Interrupt Main Routine
    *(vu32 *)INTR_VECTOR_BUF = (vu32 )IntrMainBuf;

    DmaArrayCopy(3, CharData_Sample, BG_VRAM+0x8000, 32);  //  Set BG Character
    DmaArrayCopy(3, CharData_Sample, OBJ_MODE0_VRAM, 32);  // Set OBJ Character
    DmaArrayCopy(3, PlttData_Sample, BG_PLTT,        32);  //  Set BG Palette
    DmaArrayCopy(3, PlttData_Sample, OBJ_PLTT,       32);  // Set OBJ Palette

    DmaArrayCopy(3, BgScData_Sample, BgBak,          32);  // Set BG Screen
    DmaArrayCopy(3, BgBak,           BG_VRAM,        32);

    DmaArrayClear(3,160,             OamBak,         32);  // Undisplayed OBJ moves outside screen
    DmaClear(     3,160,             OAM, OAM_SIZE,  32);
    DmaArrayCopy(3, OamData_Sample,  OamBak,         32);  // Set OAM
//    DmaArrayCopy(3, OamBak,          OAM,            32);

    for (i=0; i<MULTI_SIO_PLAYERS_MAX; i++)     OamBak[i].VPos = 200;

    *(vu16 *)REG_BG0CNT =                                  // Set BG Control
            BG_COLOR_16 | BG_SCREEN_SIZE_0 | BG_PRIORITY_0
            | 0 << BG_SCREEN_BASE_SHIFT | 2 << BG_CHAR_BASE_SHIFT ;

    *(vu16 *)REG_IE    = V_BLANK_INTR_FLAG            // Enable V-Blank Interrupt
                       | CASSETTE_INTR_FLAG;          // Enbale Game Pak Interrupt
    *(vu16 *)REG_STAT  = STAT_V_BLANK_IF_ENABLE;
    *(vu16 *)REG_IME   = 1;                                // Set IME

    *(vu16 *)REG_DISPCNT = DISP_MODE_0 | DISP_BG0_ON | DISP_OBJ_ON; // LCDC ON

    MultiSioInit();                                        // Muli-play Communication Initialization
    UsrSendBuf[0] = 200;

    *(vu16 *)REG_DISPCNT = DISP_MODE_0 | DISP_BG0_ON | DISP_OBJ_ON; // LCDC ON

    while(1) {
        if (!(SioFlags & MULTI_SIO_CONNECTED_ID_MASK)   // Master or prior to connection determination
          || (SioFlags & MULTI_SIO_PARENT))
                VBlankIntrWait();                       // Wait for end V-Blank interrupt
        else    IntrWait(1, SIO_INTR_FLAG);             // Wait for SIO Interrupt End

        SioFlags = MultiSioMain(UsrSendBuf, UsrRecvBuf);// Multi-play Communication Main
        if (Trg)   MultiSioStart();                     // Multi-play Communication Start

        KeyRead();                                      // Key Operation

        if (SioFlags & MULTI_SIO_PARENT) {
            if (!SioStartFlag)    StrTmp = "PUSH ANY KEY  ";
            else                  StrTmp = "              ";
            BgScSet(StrTmp, &BgBak[32*11+7], 1);
        }

        if (!SioStartFlag) {                            // After confirm connect
            if (SioFlags & 0xf) {                       // initialize OBJ location
                MultiSioId = ((SioMultiCnt *)REG_SIOCNT)->ID;
                UsrSendBuf[0] = ((OamData *)OamData_Sample)[MultiSioId].VPos;
                UsrSendBuf[1] = ((OamData *)OamData_Sample)[MultiSioId].HPos;
                SioStartFlag = 1;
            }
        } else {
            for (i=0; i<4; i++)
                if (SioFlags & (1 << i)) {
                    OamBak[i].VPos = UsrRecvBuf[i][0];
                    OamBak[i].HPos = UsrRecvBuf[i][1];
                }

            if (SioFlags & 0xe
              && SioFlags & (1 << MultiSioId)) {
#ifndef KEY_ON
                UsrSendBuf[1] -= 4;
#else
                if (Cont & U_KEY)    UsrSendBuf[0] -= 4;
                if (Cont & D_KEY)    UsrSendBuf[0] += 4;
                if (Cont & L_KEY)    UsrSendBuf[1] -= 4;
                if (Cont & R_KEY)    UsrSendBuf[1] += 4;
#endif
            }
        }
    }
}


/*==================================================================*/
/*                      Interrupt Routine                           */
/*==================================================================*/

/*------------------------------------------------------------------*/
/*                      V-Blank Processing                          */
/*------------------------------------------------------------------*/

void VBlankIntr(void)
{
    if (SioFlags & MULTI_SIO_PARENT)                // Start Multi-play Communication (1 frame)
        MultiSioVSync();

    // To enable SIO interrupt do not use DMA
    CpuFastArrayCopy(BgBak,  BG_VRAM);              // Set BG Screen
    CpuFastArrayCopy(OamBak, OAM    );              // Set OAM

    *(vu16 *)REG_IME = 0;
    *(vu16 *)INTR_CHECK_BUF |= V_BLANK_INTR_FLAG;   // Set V-Blank Interrupt Check
    *(vu16 *)REG_IME = 1;
}

/*------------------------------------------------------------------*/
/*                      Interrupt Dummy Routine                     */
/*------------------------------------------------------------------*/

void IntrDummy(void)
{
}

/*==================================================================*/
/*                     Subroutine                                   */
/*==================================================================*/

/*------------------------------------------------------------------*/
/*                      Key Read                                    */
/*------------------------------------------------------------------*/

void KeyRead(void)
{
    u16 ReadData = (*(vu16 *)REG_KEYINPUT ^ 0x03ff);
    Trg  = ReadData & (ReadData ^ Cont);            // Trigger Input
    Cont = ReadData;                                //   Hold Input
}

/*------------------------------------------------------------------*/
/*                      Set BG Screen                               */
/*------------------------------------------------------------------*/

void BgScSet(u8 *Srcp, u16 *Destp, u8 PlttNo)
{
    while (*Srcp != '\0')
        *Destp++ = (u16 )PlttNo << BG_SC_PLTT_SHIFT | *Srcp++;
}


